package com.joysuch.wwyt.core.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.joysuch.wwyt.config.RedisClient;
import com.joysuch.wwyt.core.bean.UserInfoBean;
import com.joysuch.wwyt.core.bean.UsernamePasswordTokenWithCompanyCode;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.core.constant.Constants;
import com.joysuch.wwyt.core.controller.LoginController;
import com.joysuch.wwyt.core.entity.BaseConfig;
import com.joysuch.wwyt.core.entity.BaseLoginAccount;
import com.joysuch.wwyt.core.entity.BaseModuleMenuMapping;
import com.joysuch.wwyt.core.entity.BaseUser;
import com.joysuch.wwyt.core.mapper.BaseModuleMenuMappingMapper;
import com.joysuch.wwyt.core.repository.BaseLoginAccountDao;
import com.joysuch.wwyt.core.repository.BaseUserDao;
import com.joysuch.wwyt.core.security.ShiroUser;
import com.joysuch.wwyt.core.service.BaseConfigService;
import com.joysuch.wwyt.core.service.BaseLoginService;
import com.joysuch.wwyt.core.service.BaseModuleMenuMappingService;
import com.joysuch.wwyt.core.service.BaseUserService;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.licence.bean.LicenceCacheDto;
import com.joysuch.wwyt.licence.constant.LicenceStateEnum;
import com.joysuch.wwyt.licence.service.LicenceService;
import com.joysuch.wwyt.util.AESUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
@RequiredArgsConstructor
public class BaseLoginServiceImpl implements BaseLoginService {

    private static final Logger logger = LoggerFactory.getLogger(BaseLoginServiceImpl.class);

    private final BaseUserDao baseUserDao;

    private final BaseLoginAccountDao baseLoginAccountDao;

    private final LicenceService licenceService;

    private final RedisClient redisClient;

    private final BaseUserService baseUserService;

    private final BaseConfigService baseConfigService;

    @Override
    public ModelAndView doLogin(String name, String key, HttpServletRequest request) {
        String loginType = findLoginType(request);
        String companyCode = findCompanyCode(request);
        String clientId = findClientId(request);

        // 对用户名密码进行解密
        String[] decode = decryptAES(name, key);
        String username = decode[0];
        String password = decode[1];

        String loginKey = getLoginKey(username, companyCode);
        String s = redisClient.get(loginKey);
        if (s != null && 5 == Long.parseLong(s)) {
            long expireTime = redisClient.getExpireTime(loginKey);
            String message = getRetryMessage(expireTime);
            ModelAndView mav = new ModelAndView();
            MappingJackson2JsonView view = new MappingJackson2JsonView();
            view.setAttributesMap(ResultBean.failInMap(102, "用户名或密码输入错误" + message));
            mav.setView(view);
            return mav;
        }

        // 如果已经登录并且 tokenId 有效， bypass login
        String tokenId = request.getHeader(Constants.AUTHORIZATION);
        if (tokenId != null) {
            Object sessionToken = request.getSession().getAttribute(Constants.AUTHORIZATION);
            if (sessionToken != null && tokenId.equals(sessionToken.toString())) {
                ShiroUser shiroUser = (ShiroUser) request.getSession().getAttribute("user");
                return loginSuccess(request, shiroUser, username, companyCode, clientId);
            }
        }

        UsernamePasswordTokenWithCompanyCode token = new UsernamePasswordTokenWithCompanyCode(username, password, loginType, null);

        Subject subject = SecurityUtils.getSubject();
        try {
            // token.setRememberMe(true);
            token.setCompanyCode(companyCode);
            subject.login(token);// 登陆成功的话，放到session中
            ShiroUser user = (ShiroUser) subject.getPrincipal();
            baseUserService.completeShiroUserInfo(user.getRealUserId(), user);
            // 设置公司编码
            token.setCompanyCode(user.getCompanyCode());
            request.getSession().setAttribute("user", user);
            // 登录成功后添加TokenId
            tokenId = request.getSession().getId();
            request.getSession().setAttribute(Constants.AUTHORIZATION, tokenId);
            return loginSuccess(request, user, username, companyCode, clientId);
        } catch (Exception e) {
            logger.error("登录出错", e);
            return loginFailed(request, e.getMessage(), username, companyCode);
        }
    }


    @Override
    public Object logout(HttpServletRequest request) {
        Subject subject = SecurityUtils.getSubject();
        String companyCode = findCompanyCode(request);
        try {
            subject.logout();
            return loginSuccess(request, null, null, companyCode,null);
        } catch (Exception e) {
            logger.error("登出系统出错", e);
            return ResultBean.fail(106, "登出系统出错:" + e.getMessage());
        }
    }

    @Override
    public ModelAndView doLoginByUserCode(String code, HttpServletRequest request) {
        Long tenentId = 1L;
        String companyCode = "001-001";
        //AES解密得到解密过后的key
        String userCode = AESUtil.decrypt(code, Constants.LOGIN_AES_KEY);
        //拿到code查看是否存在,存在则拿到人员信息,不存在直接提示
        BaseUser baseUser = baseUserDao.findByCodeAndTenentIdAndOrgCode(userCode, tenentId, companyCode);
        if (null == baseUser) {
            return loginFailed(request, "用户不存在", null, null);
        }
        //根据人员信息查找其账号
        BaseLoginAccount userAccount = baseLoginAccountDao.findByBaseUser(baseUser);
        if (null == userAccount) {
            return loginFailed(request, "用户暂无账号", null, null);
        }
        String clientId = findClientId(request);
        String tokenId = request.getHeader(Constants.AUTHORIZATION);
        UsernamePasswordTokenWithCompanyCode token = new UsernamePasswordTokenWithCompanyCode(userAccount.getUserName(), null, Constants.LOGIN_TYPE_NOPASSWORD, null);
        Subject subject = SecurityUtils.getSubject();
        try {
            // token.setRememberMe(true);
            token.setCompanyCode(companyCode);
            subject.login(token);// 登陆成功的话，放到session中
            ShiroUser user = (ShiroUser) subject.getPrincipal();
            baseUserService.completeShiroUserInfo(user.getRealUserId(), user);
            // 设置公司编码
            token.setCompanyCode(user.getCompanyCode());
            request.getSession().setAttribute("user", user);
            // 登录成功后添加TokenId
            tokenId = request.getSession().getId();
            request.getSession().setAttribute(Constants.AUTHORIZATION, tokenId);
            return loginSuccess(request, user, userAccount.getUserName(), companyCode, clientId);
        } catch (Exception e) {
            logger.error("登录出错", e);
            return loginFailed(request, e.getMessage(), userAccount.getUserName(), companyCode);
        }
    }

    @Override
    public ModelAndView loginSuccess(HttpServletRequest request, ShiroUser shiroUser, String userName, String companyCode, String clientId) {
        ModelAndView mav = new ModelAndView();
        MappingJackson2JsonView view = new MappingJackson2JsonView();
        Object token = request.getSession().getAttribute(Constants.AUTHORIZATION);
        view.setAttributesMap(ResultBean.successInMap(token));
        mav.setView(view);
        // 登录成功
        if (shiroUser != null) {
            Context.setCurrentUser(shiroUser);
            UserInfoBean userInfo = getLoginUserInfo(shiroUser);
            // 第一次登录需要设置新密码
            Long realUserId = shiroUser.getRealUserId();
            BaseUser baseUser = baseUserDao.findById(realUserId).get();
            BaseLoginAccount baseLoginAccount = baseLoginAccountDao.findByBaseUserAndShowState(baseUser.getId(), 1);
            if (baseLoginAccount == null) {
                view.setAttributesMap(ResultBean.failInMap(102, "该用户账号已关闭"));
                mav.setView(view);
                return mav;
            }

            // 检查 licence
            // 如果licence解析结果不存在，则认为信息丢失或被篡改，需要重新上传解析licence
            LicenceCacheDto licenceCacheDto = licenceService.getLicenceCacheDto(shiroUser.getTenentId(), shiroUser.getCompanyCode());
            if (licenceCacheDto == null) {
                view.setAttributesMap(ResultBean.failInMap(102, "企业授权信息不存在，请联系实施工程师"));
                return mav;
            }

            // 没有在有效期的模块则报错
            if (!licenceCacheDto.isAllow()) {
                int state = licenceCacheDto.getState();
                String stateMsg = licenceCacheDto.getStateMsg();
                view.setAttributesMap(ResultBean.failInMap(state, StrUtil.isBlank(stateMsg) ? LicenceStateEnum.value2Desc(state) : stateMsg));
                return mav;
            }

            baseLoginAccount.setLoginHistory(1);
            if (StringUtils.isNotBlank(clientId)) {
                baseLoginAccount.setClientId(clientId);
            }
            Integer firstLogin = baseLoginAccount.getFirstLogin();
            userInfo.setFirstLogin(firstLogin);
            userInfo.setToken(token == null ? "" : token.toString());
            baseLoginAccountDao.save(baseLoginAccount);
            mav.addObject("data", userInfo);
        }
        // 登录成功 清缓存
        if (shiroUser != null) {
            redisClient.removeKey(getLoginKey(userName, companyCode));
        }
        return mav;
    }


    @Override
    public ModelAndView loginFailed(HttpServletRequest request, String string, String userName, String companyCode) {
        ModelAndView mav = new ModelAndView();
        MappingJackson2JsonView view = new MappingJackson2JsonView();
        view.setAttributesMap(ResultBean.failInMap(102, "用户名或密码错误"));
        if (null == baseLoginAccountDao.findFirstByUserName(userName)) {
            view.setAttributesMap(ResultBean.failInMap(102, "用户名不存在"));
        }
        mav.setView(view);
        // 登录失败 记录次数
        String loginKey = getLoginKey(userName, companyCode);
        String s = redisClient.get(loginKey);
        if (s == null) {
            redisClient.set(loginKey, String.valueOf(1));
        } else {
            long times = Long.parseLong(s);
            if (times == 5) {
                long expireTime = redisClient.getExpireTime(loginKey);
                String message = getRetryMessage(expireTime);
                view.setAttributesMap(ResultBean.failInMap(102, "用户名或密码输入错误" + message));
            } else {
                long l = ++times;
                if (l == 5) {
                    view.setAttributesMap(ResultBean.failInMap(102, "用户名或密码输入错误，5分钟后再尝试登录"));
                    redisClient.setEx(loginKey, String.valueOf(l), 5, TimeUnit.MINUTES);
                } else {
                    redisClient.set(loginKey, String.valueOf(l));
                }
            }
        }
        return mav;
    }


    private UserInfoBean getLoginUserInfo(ShiroUser user) {
        UserInfoBean bean = new UserInfoBean();
        bean.setId(user.getRealUserId());
        bean.setCompany(user.getCompanyName());
        bean.setCompanyId(user.getCompanyId());
        bean.setCompanyCode(user.getCompanyCode());
        bean.setJobId(user.getJobId());
        bean.setJobName(user.getJobName());
        bean.setName(user.getUsername());
        bean.setRealName(user.getRealName());
        bean.setDepartId(user.getDepartId());
        bean.setDepartName(user.getDepartName());
        bean.setRoleIds(user.getRoleIds());
        bean.setTenentId(user.getTenentId());
        bean.setUserCode(user.getCode());
        try {
            // 获取配置信息
            List<BaseConfig> configList = baseConfigService.findConfigList(user.getTenentId(), user.getCompanyCode());
            if (configList != null) {
                Map<String, String> configMap = new HashMap<>();
                configList.stream().forEach(item -> {
                    configMap.put(item.getCode(), item.getValue());
                });
                bean.setConfigMap(configMap);
            }

        } catch (Exception e) {
            logger.error("加载菜单配置出错", e);
        }
        return bean;
    }

    private static String getLoginKey(String userName, String companyCode) {
        return "login-fail-count" + ":" + companyCode + ":" + userName;
    }

    private String[] decryptAES(String data, String key) {
        if (StringUtils.isEmpty(data) || StringUtils.isEmpty(key)) {
            return new String[]{"", ""};
        }
        String content = "";
        try {
            content = AESUtil.desEncrypt(data, key, key);
        } catch (Exception e) {
            return new String[]{"", ""};
        }
        if (content == null) {
            return new String[]{"", ""};
        }
        return content.split(";");
    }

    // 获取登录重试message
    private String getRetryMessage(long expireTime) {
        long second = expireTime / 1000;// 秒
        long minute = second / 60;
        if (minute == 0) {
            if (second == 0) {
                return "";
            }
            return "," + second + "秒后再尝试登录";
        }
        return "," + minute + "分钟后再尝试登录";
    }


    private String findCompanyCode(HttpServletRequest request) {
        String companyCode = null;
        if (request.getParameter(Constants.COMPANY_CODE) != null) {
            companyCode = request.getParameter(Constants.COMPANY_CODE);
        } else if (request.getHeader(Constants.COMPANY_CODE) != null) {
            companyCode = request.getHeader(Constants.COMPANY_CODE);
        } else {
            Cookie[] cookies = request.getCookies();
            if (cookies != null) {
                for (Cookie c : cookies) {
                    if (c.getName().equals(Constants.COMPANY_CODE)) {
                        // user.setCompanyCode(c.getValue());
                        companyCode = c.getValue();
                        break;
                    }
                }
            }
        }
        return companyCode;
    }

    private String findClientId(HttpServletRequest request) {
        return request.getParameter(Constants.CLIENT_ID);
    }

    private String findLoginType(HttpServletRequest request) {
        String loginType = null;
        if (request.getParameter(Constants.LOGIN_TYPE) != null) {
            loginType = request.getParameter(Constants.LOGIN_TYPE);
        } else if (request.getHeader(Constants.LOGIN_TYPE) != null) {
            loginType = request.getHeader(Constants.LOGIN_TYPE);
        } else {
            Cookie[] cookies = request.getCookies();
            if (cookies != null) {
                for (Cookie c : cookies) {
                    if (c.getName().equals(Constants.LOGIN_TYPE)) {
                        loginType = c.getValue();
                        break;
                    }
                }
            }
        }
        return loginType;
    }
}
